﻿using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters.Binary;
using System.Linq;
using System.Text;


namespace SinsMapPacker
{

using MapPackFilesList = List<MapPackFile>;
using MapPackIndex     = Dictionary<string, List<MapPackFile>>;

[Serializable]
public partial class MapPack
{
    private static Dictionary<string, string> foldersMap = new Dictionary<string, string>()
    {
        {".galaxy", "Galaxy"},
        {".tga",    "Textures"},
    };
    
    private MapPackIndex index;


    public delegate void ModifiedEvent();

    [NonSerialized]
    public ModifiedEvent ModifiedHandler;



    public MapPack()
    {
        index = new MapPackIndex();
        foreach (string dir in foldersMap.Values)
            index[dir] = new MapPackFilesList();
    }

    public List<string> Directories
    {
        get
        {
            return new List<string>(index.Keys);
        }
    }

    public List<string> Files(string dir)
    {
        var ret = new List<string>(index[dir].Count);

        foreach (MapPackFile mpf in index[dir])
            ret.Add(mpf.filename);

        return ret;
    }

    public void AddFile(string file)
    {
        var ext = Path.GetExtension(file);

        if (!foldersMap.ContainsKey(ext))
            throw new ArgumentException(
                string.Format("Can't add file with extension '{0}' to map pack", ext));

        var folder = foldersMap[ext];

        var baseFileName = Path.GetFileName(file);
        var existingFile = index[folder].Where(mpFile => mpFile.filename == baseFileName);
        if ( existingFile.Count() > 0 )
        {
            existingFile.First().readFile(file);
        }
        else
        {
            var mpFile = new MapPackFile();
            mpFile.readFile(file);

            index[folder].Add(mpFile);
        }

        ModifiedHandler();
    }

    public void RemoveFile(string file)
    {
        var ext = Path.GetExtension(file);
        if ( !foldersMap.ContainsKey(ext) )
            return;

        file = Path.GetFileName(file);
        var found = index[foldersMap[ext]].Where(mapFile => mapFile.filename == file);
        if ( found.Count() > 0 )
            index[foldersMap[ext]].Remove(found.First());

        ModifiedHandler();
    }

    public static MapPack Open(string file)
    {
        var fs = new FileStream(file, FileMode.Open);
        var formatter = new BinaryFormatter();

        MapPack mp = (MapPack)formatter.Deserialize(fs);
        fs.Close();

        return mp;
    }

    public void Write(string file)
    {
        var tmpFile = Path.GetTempFileName();

        var fs = new FileStream(tmpFile, FileMode.Create);
        var formatter = new BinaryFormatter();
        formatter.Serialize(fs, this);
        fs.Close();

        if (File.Exists(file))
        {
            var backup = Path.GetTempFileName();
            File.Replace(tmpFile, file, backup);
            File.Delete(backup);
        }
        else
        {
            File.Move(tmpFile, file);
        }
    }

    public void Unpack(string baseDir)
    {
        if (!Directory.Exists(baseDir))
            throw new ArgumentException(string.Format("{0} does not exist", baseDir));

        foreach (var idxEntry in index)
        {
            var targetDir = baseDir + "/" + idxEntry.Key;

            if (!Directory.Exists(targetDir))
                Directory.CreateDirectory(targetDir);

            if (!Directory.Exists(targetDir))
                throw new ArgumentException(
                    string.Format("Can't unpack to directory {0}", targetDir));
            
            foreach (var mpFile in idxEntry.Value)
                File.WriteAllBytes(targetDir + "/" + mpFile.filename, mpFile.buffer);
        }
    }

    public static List<string> GetAvailableDirectories()
    {
        return foldersMap.Values.ToList();
    }

    public static List<string> GetAvailableExtensions()
    {
        return foldersMap.Keys.ToList();
    }
}

[Serializable]
class MapPackFile
{
    public string filename;
    public byte[] buffer;

    public MapPackFile()
    {
        filename = "";
    }

    public void readFile(string file)
    {
        try
        {
            filename = Path.GetFileName(file);
            buffer = File.ReadAllBytes(file);
        }
        catch (Exception e)
        {
            filename = "";
            buffer = null;
            throw e;
        }
    }
}

}
